﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
using System.Reflection;
using Elf.Data.Entity.Configuration;

namespace Elf.Data.Entity
{
    /// <summary>
    /// Class to offer facilicities for database access.
    /// </summary>
    public abstract class ContextBase : DbContext
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="BaseContext"/> class.
        /// </summary>
        protected ContextBase()
            : base()
        {
            Configuration.LazyLoadingEnabled = true;
            Configuration.ProxyCreationEnabled = true;
            Configuration.ValidateOnSaveEnabled = false;
        }
        #endregion

        #region Model Creation
        /// <summary>
        /// This method is called when the model for a derived context has been initialized, but
        /// before the model has been locked down and used to initialize the context.  The default
        /// implementation of this method does nothing, but it can be overridden in a derived class
        /// such that the model can be further configured before it is locked down.
        /// </summary>
        /// <param name="modelBuilder">The builder that defines the model for the context being created.</param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            if (modelBuilder == null) throw new ArgumentNullException("modelBuilder");

            var contextConfiguration = ContextConfiguration.Instance;

            using (var catalog = new DirectoryCatalog(AppDomain.CurrentDomain.RelativeSearchPath))
            {
                using (var container = new CompositionContainer(catalog))
                {
                    container.ComposeParts(contextConfiguration);

                    foreach (var configuration in
                        contextConfiguration.Configurations)
                    {
                        configuration.AddConfiguration(
                            modelBuilder.Configurations);
                    }
                }
            }

            base.OnModelCreating(modelBuilder);
        }
        #endregion

        #region Saving
        /// <summary>
        /// Executes before SaveChanges is executed.
        /// </summary>
        public abstract void BeforeSaveChanges();

        /// <summary>
        /// Saves all changes made in this context to the underlying database.
        /// </summary>
        /// <returns>
        /// The number of objects written to the underlying database.
        /// </returns>
        /// <exception cref="T:System.InvalidOperationException">Thrown if the context has been disposed.</exception>
        public override int SaveChanges()
        {
            // Make sure all changes are detected
            ChangeTracker.DetectChanges();

            // Enable the possibility to have actions before saving
            BeforeSaveChanges();

            // Make sure all objects are validated
            Validate();

            // Save changes
            return base.SaveChanges();
        }
        #endregion

        #region Validation
        /// <summary>
        /// Validates entities in the current context. Should throw a validation 
        /// exception when entities are in an invalid state.
        /// </summary>
        public abstract void Validate();
        #endregion
    }
}